#include <boost/random.hpp>
#include <kuai/misc/thread.hpp>
#include <kuai/misc/xyz.hpp>


#ifndef _KUAI_SIM_SIMJOB_HPP_
#define _KUAI_SIM_SIMJOB_HPP_

namespace kuai {

	
	class EnergySet;
	class ControlSet;

	class SimulationJob
	{
	public:
		SimulationJob();
		virtual ~SimulationJob();

	public:
		int random_seed;

		virtual void addE(EnergySet& efunc, RealNumber e[]);
		virtual void addEG(EnergySet& efunc, RealNumber e[], XYZ g[]);
		virtual void addEGH(EnergySet& efunc, RealNumber e[], XYZ g[], RealNumber* hess[]);
		virtual void addHX(EnergySet& efunc, const RealNumber x[], RealNumber result[]);
		virtual void run(EnergySet& efunc, ControlSet& controls, size_t nsteps=size_t(-1));

		bool stop() const {
			return stopIteration;
		}
		void stop(bool stopIt) {
			stopIteration = stopIt;
		}

		void lock() {
			assert (mutex != NULL);
			mutex->lock();
		}

		void free() {
			assert (mutex != NULL);
			mutex->unlock();
		}

		void try_lock() {
			assert (mutex != NULL);
			mutex->try_lock();
		}

		void wait() {
			assert (barrier != NULL);
			barrier->wait();
		}

		void interrupt_all() {
			assert (group != NULL);
			group->interrupt_all();
		}

#ifndef USE_DUMN_THREAD
	public:
		size_t getCPU() const {
			return cpu;
		}
		void setCPU(size_t i);
#else
	public:
		size_t getCPU() const {
			return 1;
		}
		void setCPU(size_t i) 
		{ }
#endif

	protected:
		template<typename SimEnvType>
			void runjob(std::vector<SimEnvType>& v)
		{
			assert (v.size() == cpu);
			assert (mutex == NULL);
			assert (barrier == NULL);
			assert (group == NULL);

			mutex= new Mutex;
			barrier = new Barrier(cpu);
			group = new ThreadGroup();

			boost::rand48 rand(random_seed);
			for (Index i = 0; i < cpu; ++i)
			{
				v[i].job=this;
				v[i].tid = i;
				v[i].setSeed(rand());
			}
			try {
				for (Index i = 0; i < cpu; ++i)
				{
					group->create_thread<SimEnvType&>(v[i]);
				}
				group->join_all();
				delete mutex;
				mutex = NULL;

				delete barrier;
				barrier = NULL;

				delete group;
				group = NULL;
			}
			catch (...) {
				group->interrupt_all();
				for (Index i = 0; i < cpu; ++i)
				{
					v[i].finalize();
				}
				delete mutex;
				mutex = NULL;

				delete barrier;
				barrier = NULL;

				delete group;
				group = NULL;
				throw;
			}
		}

	private:
		size_t cpu;
		bool stopIteration;

		Mutex* mutex;
		Barrier* barrier;
		ThreadGroup* group;
	};

	struct SimulationEnvironment
	{
	public:
		boost::variate_generator<boost::mt19937&, boost::uniform_01<RealNumber> > die_01;
		boost::variate_generator<boost::mt19937&, boost::normal_distribution<RealNumber> > die_norm;

	public:
		explicit SimulationEnvironment(int seed = 0)
			: gen(seed), die_01(gen, dist_01), die_norm(gen, dist_norm)
		{ }

		inline std::pair<size_t, size_t> range(size_t all) const {
			size_t nThreads = getCPU();
			size_t n = all / nThreads;
			size_t m = all % nThreads;
			size_t result1 = n * tid;
			result1 += tid<m?tid:m;
			size_t result2 = n * (tid+1);
			result2 += size_t(tid+1)<m?size_t(tid+1):m;
			return std::make_pair(result1, result2);
		}

		void setSeed(int seed) {
			gen = boost::mt19937(seed);
		}

		bool isMainThread() const {
			return tid  == 0;
		}

		void lock() {
			job->lock();
		}
		void free() {
			job->free();
		}
		void wait() {
			job->wait();
		}
		void interrupt_all() {
			job->interrupt_all();
		}

		Index getTID() const {
			return tid;
		}

		Index getCPU() const {
			return job->getCPU();
		}

		bool stop() const {
			return job->stop();
		}
		void stop(bool stopIt) {
			job->stop(stopIt);
		}

	protected:
		SimulationJob* job;
		Index tid;

		boost::mt19937 gen;
		boost::uniform_01<RealNumber> dist_01;
		boost::normal_distribution<RealNumber> dist_norm;

		friend class SimulationJob;
	};


	/*

	class SimulationJob;

	class SimulationJobFunction 
		: public SimulationEnvironment
	{
	public:
		SimulationJobFunction(boost::mt19937& gen)
			: SimulationEnvironment(gen)
		{ }
		PhaseSpace*		space;
		EnergySet*		efunc;
		ControlSet*		controls;
		size_t nstep;
		void operator()();
	};

	class SimulationJob
		: public SequenceJob
	{
	public:
		DECLARE_JOB_CREATOR(SimulationJob);

	public:
		SimulationJob();

	public:
		virtual void pushParameter(const String& key, const String& data, std::ostream& output, std::ostream& error);
		virtual void doJob(std::ostream& output, std::ostream& error);

	protected:
		PhaseSpace	space;
		size_t		cpu;
		int			seed;

		virtual void run(std::ostream& output, std::ostream& error, size_t n);
	};

	/*
	class AnalyzeJob
		: public SimulationJob
	{
	public:
		DECLARE_JOB_CREATOR(AnalyzeJob);

	public:
		virtual void pushParameter(const String& key, const String& data, std::ostream& output, std::ostream& error);
		virtual void doJob(std::ostream& output, std::ostream& error);

	protected:
		boost::shared_ptr<TrajecPlayer> player;
		virtual void run(std::ostream& output, std::ostream& error, size_t n);
	};
	*/

}

#endif
